﻿#pragma once

#include "Render/VertexBuffer.h"
#include "Render/IndexBuffer.h"

// std
#include <vector>

struct VertexAttribute {
  enum ComponentType {
    UShort = GL_UNSIGNED_SHORT,
    Int = GL_INT,
    UInt = GL_UNSIGNED_INT,
    Byte = GL_BYTE,
    Float = GL_FLOAT,
  };

  uint8_t componentCount;
  ComponentType type;
  bool shouldBeNormalized = false;
  int32_t vertexSize;

  VertexAttribute(uint8_t componentCount, ComponentType type)
      : componentCount(componentCount),
        type(type),
        shouldBeNormalized(false),
        vertexSize(0) {}
};

class CORE_API VertexArray
{
public:
  VertexArray();
  ~VertexArray();

  template<typename VertexT>
  explicit VertexArray(const std::vector<VertexT> &vertices, bool dynamic = false) {
    glGenVertexArrays(1, &m_id);
    Bind();

    m_vertexBuffer = CreateRef<VertexBuffer>();
    if (dynamic) {
      m_vertexBuffer->BufferDynamicData<VertexT>(vertices);
    } else {
      m_vertexBuffer->BufferStaticData<VertexT>(vertices);
    }

    Unbind();
  };


  template<typename VertexT, typename IndexT>
  VertexArray(const std::vector<VertexT> &vertices, const std::vector<IndexT> &indices, bool dynamic = false)
      : VertexArray(vertices, dynamic) {
    Bind();
    m_indexBuffer = CreateRef<IndexBuffer>(IndexT());

    if (dynamic) {
      m_indexBuffer->BufferDynamicData<IndexT>(indices);
    } else {
      m_indexBuffer->BufferStaticData<IndexT>(indices);
    }
    Unbind();
  };

  template<typename VertexT>
  explicit VertexArray(const std::vector<VertexT> &vertices,
                       const std::vector<VertexAttribute> &vertexAttributes,
                       bool dynamic = false)
      : VertexArray(vertices, dynamic) {
    AddVertexAttributes(vertexAttributes, sizeof(VertexT));
  };

  template<typename VertexT, typename IndexT>
  VertexArray(const std::vector<VertexT> &vertices,
              const std::vector<VertexAttribute> &vertexAttributes,
              const std::vector<IndexT> &indices,
              bool dynamic = false)
      : VertexArray(vertices, indices, dynamic) {
    AddVertexAttributes(vertexAttributes, sizeof(VertexT));
  };

  bool IsValid() const { return m_id != 0; }

  void Bind() const;
  void Unbind() const;

  void DrawIndexed(int32_t type = GL_TRIANGLES) const;
  void DrawVertex(int32_t type = GL_TRIANGLES) const;
  void DrawVertexSub(int32_t size, int32_t startOffset = 0, int32_t type = GL_TRIANGLES) const;
  
  void AddVertexAttributes(const std::vector<VertexAttribute> &vector, int32_t defaultVertexSize);

  Ref<VertexBuffer> GetVertexBuffer() const { return m_vertexBuffer; }
  Ref<IndexBuffer> GetIndexBuffer() const { return m_indexBuffer; }

private:
  GLuint m_id;
  Ref<VertexBuffer> m_vertexBuffer;
  Ref<IndexBuffer> m_indexBuffer;
};